#include "opencldialog.h"

#include <QDebug>
#include <exception>
#include <stdexcept>

/// Parameter:
/// Return:
/// Action: Listener for platform selection
///         After a platform is selected, gets the devices for that platform and displays them.
void OpenCLDialog::platform_clicked()
{
    if (_deviceBox != 0)
    {
        for (unsigned int i = 0; i < _deviceRB.size(); ++i)
        {
            _deviceGroup->removeButton(_deviceRB[i]);
            _deviceBoxLayout->removeWidget(_deviceRB[i]);
            delete _deviceRB[i];
        }
        _mainLayout->removeWidget(_deviceBox);
        delete _deviceBoxLayout;
        delete _deviceBox;
        delete _deviceGroup;

        _devices.clear();
        _deviceRB.clear();
    }

    int ind = _platformGroup->checkedId();

    _selectedPlatform = _platforms[ind];

    int error = _selectedPlatform.getDevices(CL_DEVICE_TYPE_ALL, &_devices);
    if (error != CL_SUCCESS)
    {
        std::runtime_error ex("Error while querying devices.");
        throw ex;
    }

    _deviceGroup = new QButtonGroup;
    _deviceBox = new QGroupBox("Avalaible devices");
    _deviceBoxLayout = new QVBoxLayout;

    for (unsigned int i = 0; i < _devices.size(); ++i)
    {
        QRadioButton* rb = new QRadioButton(QString::fromStdString(_devices[i].getInfo<CL_DEVICE_NAME>()));
        connect(rb, SIGNAL(toggled(bool)), this, SLOT(device_clicked()));
        _deviceGroup->addButton(rb, i);
        _deviceRB.push_back(rb);
        _deviceBoxLayout->addWidget(rb);
    }

    if (_devices.size() != 0)
        _deviceRB[0]->setChecked(true);

    _deviceBox->setLayout(_deviceBoxLayout);

    if (!_platformBox->isEnabled())
        _deviceBox->setEnabled(false);

    _mainLayout->addWidget(_deviceBox);
}

/// Parameter:
/// Return:
/// Action: Listener for device selection.
void OpenCLDialog::device_clicked()
{
    int ind = _deviceGroup->checkedId();

    _selectedDevice = _devices[ind];
}

/// Parameter:
/// Return:
/// Action: Listener for enable/disable button
void OpenCLDialog::enabled_changed()
{
    if (_enabled->isChecked())
    {
        _platformBox->setEnabled(true);

        if (_deviceBox != 0)
            _deviceBox->setEnabled(true);
    }
    else
    {
        _platformBox->setEnabled(false);

        if (_deviceBox != 0)
            _deviceBox->setEnabled(false);
    }
}

/// Parameter: platforms: Initial platforms to display
///            parent: Parent widget
/// Return:
/// Action: Constructor
///         Constructs the dialog and sets the members.
OpenCLDialog::OpenCLDialog(const std::vector<cl::Platform> platforms, QWidget *parent) : QDialog(parent)
{
    _enabled = new QCheckBox("OpenCL Enable / Disable");
    connect(_enabled, SIGNAL(stateChanged(int)), this, SLOT(enabled_changed()));

    _okButton = new QPushButton("Ok");
    connect(_okButton, SIGNAL(clicked()), this, SLOT(accept()));

    _platformGroup = new QButtonGroup;

    _platformBox = new QGroupBox("Avalaible platforms");
    _platformBoxLayout = new QVBoxLayout;

    _platforms = platforms;

    _mainLayout = new QVBoxLayout;
    _mainLayout->addWidget(_enabled);
    _mainLayout->addWidget(_okButton);

    for (unsigned int i = 0; i < platforms.size(); ++i)
    {
        QRadioButton* rb = new QRadioButton(QString::fromStdString(platforms[i].getInfo<CL_PLATFORM_NAME>()));
        connect(rb, SIGNAL(toggled(bool)), this, SLOT(platform_clicked()));
        _platformGroup->addButton(rb, i);
        _platformRB.push_back(rb);
        _platformBoxLayout->addWidget(rb);
    }

    _platformBox->setLayout(_platformBoxLayout);

    if (!_enabled->isChecked())
        _platformBox->setEnabled(false);

    _mainLayout->addWidget(_platformBox);
    setLayout(_mainLayout);

    _deviceBox = 0;
    _deviceBoxLayout = 0;
    _deviceGroup = 0;

    if (_platforms.size() != 0)
        _platformRB[0]->setChecked(true);
    else
        _enabled->setEnabled(false);

    setWindowTitle("OpenCL options");
}

/// Parameter:
/// Return: true: OpenCL is enabled, false: OpenCL is disabled
/// Action:
bool OpenCLDialog::is_enabled() const
{
    return _enabled->isChecked();
}

/// Parameter:
/// Return: the selected platform
/// Action:
cl::Platform OpenCLDialog::selected_platform() const
{
    return _selectedPlatform;
}

/// Parameter:
/// Return: the selected device
/// Action:
cl::Device OpenCLDialog::selected_device() const
{
    return _selectedDevice;
}
